package com.raysdata.rdc.dataasset.utils;


import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.gen.RSAKeyGenerator;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.bouncycastle.jce.provider.BouncyCastleProvider;


import javax.validation.constraints.AssertTrue;
import java.io.*;
import java.security.*;
import java.security.spec.InvalidKeySpecException;

import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.text.ParseException;
import java.util.Date;
import java.util.Objects;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

@Component
public class JwtUtil {


	private static String keyparBase64;//="rO0ABXNyABxjb20ubmltYnVzZHMuam9zZS5qd2suUlNBS2V5AAAAAAAAAAECAApMAAFkdAAiTGNvbS9uaW1idXNkcy9qb3NlL3V0aWwvQmFzZTY0VVJMO0wAAmRwcQB+AAFMAAJkcXEAfgABTAABZXEAfgABTAABbnEAfgABTAADb3RodAAQTGphdmEvdXRpbC9MaXN0O0wAAXBxAH4AAUwACnByaXZhdGVLZXl0ABpMamF2YS9zZWN1cml0eS9Qcml2YXRlS2V5O0wAAXFxAH4AAUwAAnFpcQB+AAF4cgAZY29tLm5pbWJ1c2RzLmpvc2UuandrLkpXSwAAAAAAAAABAgALTAADYWxndAAdTGNvbS9uaW1idXNkcy9qb3NlL0FsZ29yaXRobTtMAAhrZXlTdG9yZXQAGExqYXZhL3NlY3VyaXR5L0tleVN0b3JlO0wAA2tpZHQAEkxqYXZhL2xhbmcvU3RyaW5nO0wAA2t0eXQAH0xjb20vbmltYnVzZHMvam9zZS9qd2svS2V5VHlwZTtMAANvcHN0AA9MamF2YS91dGlsL1NldDtMAAlwYXJzZWRYNWNxAH4AAkwAA3VzZXQAHkxjb20vbmltYnVzZHMvam9zZS9qd2svS2V5VXNlO0wAA3g1Y3EAfgACTAADeDV0cQB+AAFMAAZ4NXQyNTZxAH4AAUwAA3g1dXQADkxqYXZhL25ldC9VUkk7eHBwcHBzcgAdY29tLm5pbWJ1c2RzLmpvc2UuandrLktleVR5cGUAAAAAAAAAAQIAAkwAC3JlcXVpcmVtZW50dAAfTGNvbS9uaW1idXNkcy9qb3NlL1JlcXVpcmVtZW50O0wABXZhbHVlcQB+AAd4cH5yAB1jb20ubmltYnVzZHMuam9zZS5SZXF1aXJlbWVudAAAAAAAAAAAEgAAeHIADmphdmEubGFuZy5FbnVtAAAAAAAAAAASAAB4cHQACFJFUVVJUkVEdAADUlNBcHBwcHBwcHNyACBjb20ubmltYnVzZHMuam9zZS51dGlsLkJhc2U2NFVSTPmRXmh9XKr+AgAAeHIAHWNvbS5uaW1idXNkcy5qb3NlLnV0aWwuQmFzZTY0AAAAAAAAAAECAAFMAAV2YWx1ZXEAfgAHeHB0AVROZmNnbHNyVHhhNnRCb21tV1BoTE82WmZHdmtIRGMySWdENlNlQm1qN181Z2JvWmNLbG1mbVJyZWRKTFozUFU1TEF4VFN1M29LYktqWmFSZ2lPUDRyRXVLMzFRRlJrSlU1bEJqOW1qSFVGUTlUeFdYSkFlOXVnY3ZmWFU5Q2pUbWJFdjhlNmhMVmJBR2FURmZaUTdsY0dJZm96ZHlsVUMzcXFOdXNGdmJqc2hXQTlaVlpNc3A5YW5DV3lsUTVzemZMWEtoU2dtLS1pWkphZk12VkR2WXo4MTNucm15VVpNejM3TnNBaWlpTmhPZnZWTnpPdkxzeDNaT1ZaZGlfalR5TEZIMFhtelNNN25IYkEyNUlEWFNpRHRhQnpIa1NqTUNqWDBxc2NYT0k3Z2dBcEM5Zm9qT3YzWFVyUVMxOHpicEhMUVg1WTRTMXJfdUxQdWZid01ac3EAfgAVdACrUUVKejBzNmtObC10djJRbGdNSVRlS1hKcHExNEVpMzV4OTRDRkF0U21nVTBJdmZGQlBTbnRJZ1ZNWGREOF8xeVk3YWJ6anJOY19xZTNiRlVCLVoydlFFM2V4eHI2eFR6QkhuZ25vMmptTUQ1aTdmdU95SjJUUDU2N084R3ByQ2l0XzUwMTI4SUl6U0FaUlhNWDd3c0hkTWdwblAzR1JSMjJhZ2JTQXM0SWtjc3EAfgAVdACrSkhNWUluMUZwQjJHZ2Jwa2x5R1BRTjQ1Z3pUZHRUblhMbF9KaHVaUjhIS1k1Nm1HWmR6Vm5Wc2RhQjVRekdnSW52UTZVWUpuWk1TTzY3OUpoS1BzREhzcXB6cEtlNUNhdFVfVDltNWlqTGpKR19FS1YySmNkQl9Fd1BzMUNtMXJETld2ZWg0TFozcWFGVnR1dDdCcGx1Uk9tcnE3c0ppcWhyRnhTaXFjUEJVc3EAfgAVdAAEQVFBQnNxAH4AFXQBVnMydURnV1VmekhfVHp0Y0dSSTNFMUplZXhVem8wT1FIcU5HeWxJVlo1Qy1YQmxKc3J6NTNoQldBTTFkMTBORzk4RWQ0MGswaGphV1NtZDRBbG9BRk1zR01NWEN1NHdJRDJrQWlLbzRaclQydkFST2RBaEE3Q1RUbklBLTRneVJmTHpNN0hMekVJUy05MVlEeFZJeWFsb291bldRRi02M2h6MUFmQXFGbXlaMlhJc19uMWJ6N1cwTENXZnBUQjBXMExwQ3F4V3VFanQ1SjB0UGhON19JZDNjdjRmbUp5d3BUSFVSTUF2enhqZDZ5YmUwbEpkcks2OVkxX3RwQUdFVUQyNGl0Nkd0NDVKVUZEbi1tRFBZY3QwZFVQY19CQU56MWF1Rkt3VXdOVGhoRmFoLTRac0g5ZnZKUHpiMGY1T2VaYnRIenF2eGs5b1lsSTIzaDIwRUdCd3NyAB9qYXZhLnV0aWwuQ29sbGVjdGlvbnMkRW1wdHlMaXN0ergXtDynnt4CAAB4cHNxAH4AFXQAqzdudkdmcWE2aUFJSUJJenNJMDdvNDFRM2NWYVNVNENvdGs4MXhRU2lSSzdyM2NrN0RtUUNSMW0za1RGVENxenNqQ3VLcFVYaWZ1Z1Q4OFI0emoxTlRkVTl4cDY1X010Vm1IbjJwQ3FKTkRZaXR5eTQyYkhoLTg2bXl0YmR6RHhpcFU5cFVXMVFpSkhWSkN3bVN6OGh0SVMyQzZLcHFFbWtuUktrWGhlRU5fTXBzcQB+ABV0AKt3SmtwZjdBX3dfWVFkSmdmNWw0MXJGcnl6SmZaRXhVMHN1TV9CMUw0Vzd4cEpYaUU2UGhxRFppbVdBdVQwYk5kRmo4NS1RbmRMdjhsdDBTeUtoZ3B5TFdkbUllWmxKeHF0VHRUVkdlaVJuT3BfSjQ3dmMwRDg4a2RncUY5TjJNVktWRHZ3SXJRM21DV1FuT0xra0pJQlJOSnhNcFMxZmFjZXdQRzNDVmQ4cDBzcQB+ABV0AKttbGFwM2pBYi1Tb2YwRXlqTWVpMUxadktXRXJocEJqOWFWUFplOUdpR0wxdkxVMlZXSlFLYnRkOWVjZ04yRk03dXAwRnJGSUdDVnVVbGVET0hWLWZSSHkzUHFlQV9iQ0h2R3N4WFBCWG1PdHhsN1g5a2wzZVB6c3dkclIwTkJQeHFKMmZ1U0ljUzJJT2dieS1FSTc4aTlCWDBDN2xKcFk0UUE1ekJ2eTBMXzQ=";
	@Value("${jwt.rsaKeyBase64}")
	public void setRsaKeyString(String base64Str) {
		keyparBase64 = base64Str;
		try {
			rsaKey = getKeyPairFromBase64Str(keyparBase64);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		publicRsaKey = rsaKey.toPublicJWK();
	}

	/**
	 * 创建加密key
	 */
	public static RSAKey getKey()  throws JOSEException {
		RSAKeyGenerator rsaKeyGenerator = new RSAKeyGenerator(2048);
		RSAKey rsaJWK = rsaKeyGenerator.generate();
		return rsaJWK;
	}

	/**
	 * 过期时间5秒
	 */
	private static final long EXPIRE_TIME = 1000 * 3600 * 24 * 10;
	private static RSAKey rsaKey;
	private static RSAKey publicRsaKey;


	static {
		/**
		 * 生成公钥，公钥是提供出去，让使用者校验token的签名
		 */
		try {
			//rsaKey = new RSAKeyGenerator(2048).generate();
			//rsaKey = getKeyPairFromBase64Str(keyparBase64);
			//publicRsaKey = rsaKey.toPublicJWK();
			//saveKeyPairTest(rsaKey);

		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public static void saveKeyPairTest(RSAKey rsaKey){
		try {
			KeyPair keyPair=rsaKey.toKeyPair();
			PrivateKey privateKey = keyPair.getPrivate();
			byte[] privateBT = privateKey.getEncoded();
			// base64
			saveFile("c://privateKey.txt", Base64.encodeBase64String(privateBT));
			//============================================//
			// 公钥
			PublicKey publicKey = keyPair.getPublic();
			byte[] publicBT = publicKey.getEncoded();
			// base64
			saveFile("c://publicKey.txt", Base64.encodeBase64String(publicBT));
			keyPairSerial(rsaKey);
		} catch (ConfigurationException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public static void keyPairSerial(RSAKey rsaKey) throws Exception {
		ByteArrayOutputStream b = new ByteArrayOutputStream();
		ObjectOutputStream o =  new ObjectOutputStream(b);
		o.writeObject(rsaKey);
		byte[] res = b.toByteArray();
		saveFile("c://keypair.out", Base64.encodeBase64String(res));

		o.close();
		b.close();

		ByteArrayInputStream bi = new ByteArrayInputStream(res);
		ObjectInputStream oi = new ObjectInputStream(bi);
		Object obj = oi.readObject();
		if(obj instanceof RSAKey){
			System.out.println("ok");
		}else{
			System.out.println("failed");
		};


		oi.close();
		bi.close();
	}

	public static RSAKey getKeyPairFromBase64Str(String base64Str) throws IOException, ClassNotFoundException {
		byte [] data=Base64.decodeBase64(base64Str);
		ByteArrayInputStream bi = new ByteArrayInputStream(data);
		ObjectInputStream oi = new ObjectInputStream(bi);
		Object obj = oi.readObject();
		if(obj instanceof RSAKey){
			//System.out.println("ok");
		}else{
			throw  new RuntimeException("证书有误");
		};
		return (RSAKey)obj;
	}

	public static void saveFile(String path,String key)throws Exception{
		FileOutputStream fos = new FileOutputStream(path);
		fos.write(key.getBytes());
		fos.flush();
		fos.close();
	}

	public static void saveKey(KeyPair keyPair, String publicKeyFile,
						String privateKeyFile) throws ConfigurationException {
		PublicKey pubkey = keyPair.getPublic();
		PrivateKey prikey = keyPair.getPrivate();

		// save public key
		PropertiesConfiguration publicConfig = new PropertiesConfiguration(
			new File(publicKeyFile));
		publicConfig.setProperty("PULIICKEY", toHexString(pubkey.getEncoded()));
		publicConfig.save();

		// save private key
		PropertiesConfiguration privateConfig = new PropertiesConfiguration(
			new File(privateKeyFile));
		privateConfig.setProperty("PRIVATEKEY",
			toHexString(prikey.getEncoded()));
		privateConfig.save();
	}

	public static Key loadKey(String filename, int type)
		throws ConfigurationException, NoSuchAlgorithmException,
		InvalidKeySpecException {
		PropertiesConfiguration config = new PropertiesConfiguration(filename);
		KeyFactory keyFactory = KeyFactory.getInstance("RSA",
			new BouncyCastleProvider());

		if (type == 0) {
			// privateKey
			String privateKeyValue = config.getString("PULIICKEY");
			PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(
				toBytes(privateKeyValue));
			PrivateKey privateKey = keyFactory.generatePrivate(priPKCS8);
			return privateKey;

		} else {
			// publicKey
			String privateKeyValue = config.getString("PRIVATEKEY");
			X509EncodedKeySpec bobPubKeySpec = new X509EncodedKeySpec(
				toBytes(privateKeyValue));
			PublicKey publicKey = keyFactory.generatePublic(bobPubKeySpec);
			return publicKey;
		}
	}
	public static final byte[] toBytes(String s) {
		byte[] bytes;
		bytes = new byte[s.length() / 2];
		for (int i = 0; i < bytes.length; i++) {
			bytes[i] = (byte) Integer.parseInt(s.substring(2 * i, 2 * i + 2),
				16);
		}
		return bytes;
	}
	public static String toHexString(byte[] b) {
		StringBuilder sb = new StringBuilder(b.length * 2);
		for (int i = 0; i < b.length; i++) {
			if(sb==null){
				System.out.println("null");
			}
			System.out.printf("%s",(b[i] & 0xf0) >>> 4);
			sb.append(HEXCHAR[(b[i] & 0xf0) >>> 4]);
			sb.append(HEXCHAR[b[i] & 0x0f]);
		}
		return sb.toString();
	}
	private static char[] HEXCHAR = { '0', '1', '2', '3', '4', '5', '6', '7',
		'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

	public static String buildToken(String account,Long expireTime) {
		try {
			/**
			 * 1. 生成秘钥,秘钥是token的签名方持有，不可对外泄漏
			 */
			RSASSASigner rsassaSigner = new RSASSASigner(rsaKey);

			/**
			 * 2. 建立payload 载体
			 */
			JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
				.subject("RUI_ZHI_DATA")
				.issuer("http://www.raysdata.com")
				.expirationTime(new Date(expireTime))
				.notBeforeTime(new Date(System.currentTimeMillis()))
				.claim("data",account)
				.build();

			/**
			 * 3. 建立签名
			 */
			SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), claimsSet);
			signedJWT.sign(rsassaSigner);

			/**
			 * 4. 生成token
			 */
			String token = signedJWT.serialize();
			return token;

		} catch (JOSEException e) {
			e.printStackTrace();
		}
		return null;
	}

	public static String volidToken(String token) {
		try {
			SignedJWT jwt = SignedJWT.parse(token);
			//添加私密钥匙 进行解密
			RSASSAVerifier rsassaVerifier = new RSASSAVerifier(publicRsaKey);

			//校验是否有效
			if (!jwt.verify(rsassaVerifier)) {
				throw ResultException.of(ResultException.INVALID, "Token 无效");
			}

			//校验超时
			if (new Date().after(jwt.getJWTClaimsSet().getExpirationTime())) {
				throw ResultException.of(ResultException.EXPIRED, "Token 已过期");
			}

			//获取载体中的数据
			Object account = jwt.getJWTClaimsSet().getClaim("data");


			//是否有openUid
			if (Objects.isNull(account)){
				throw ResultException.of(ResultException.NO_DATA, "账号为空");
			}
			return account.toString();
		} catch (ParseException e) {
			e.printStackTrace();
		} catch (JOSEException e) {
			e.printStackTrace();
		}
		return "";
	}

	public static  class  ResultException extends  RuntimeException{
		static public final int  INVALID=-1;
		static  public final int  EXPIRED=-2;
		static public final int  NO_DATA=-3;
		int code;
		public ResultException(int code,String msg){
			super(msg);
			this.code=code;
		}
		public static ResultException of(int code,String msg){
			return  new ResultException(code,msg);

		}
		public int getCode(){
			return this.code;
		}
	}

}
